In [2]:
%pylab inline
from pandas import Series, DataFrame
import pandas as pd
In [3]:
df = DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'],
'key2' : ['one', 'two', 'one', 'two', 'one'],
'data1' : np.random.randn(5),
'data2' : np.random.randn(5)}, columns = ['key1', 'key2', 'data1', 'data2'])
df
Out[3]:
In [4]:
# 使用 groupby方法
grouped = df.data1.groupby(df.key1)
grouped
# 產生一個 SeriesGroupBy物件
Out[4]:
In [5]:
grouped.size()
Out[5]:
In [6]:
# 用GroupBy物件的 mean()方法
# mean()方法是一種 聚合運算
grouped.mean()
Out[6]:
In [7]:
# 也可以建立多層次的分組
grouped = df.data1.groupby([df.key1, df.key2])
grouped.size()
Out[7]:
In [8]:
grouped.mean()
Out[8]:
In [9]:
grouped.mean().unstack('key1')
Out[9]:
In [10]:
# 也可以對多個 columns同時做分組統計運算
df.groupby(df.key1).mean()
Out[10]:
In [11]:
# 也可以直接以 column索引的名稱來指定分組
df.groupby(['key1', 'key2']).mean()
Out[11]:
In [12]:
# GroupBy 的 size()方法,傳回各分組的大小
df.groupby(['key1', 'key2']).size()
Out[12]:
In [13]:
for name, group in df.groupby('key1'):
print(name)
print(group)
# 所以分組的結果,是拆分為多個 DataFrame
In [14]:
# 依照多重鍵分組,groupby元素元組的第一個元素是 多重鍵的 元組
for name, group in df.groupby(['key1', 'key2']):
print(name)
print(group)
In [15]:
df
Out[15]:
In [16]:
df.groupby('key1')['data1']
# 等同於
df['data1'].groupby(df['key1'])
df.groupby('key1')['data2']
# 等同於
df[['data2']].groupby(df['key1'])
Out[16]:
In [17]:
# 有時候只需要對部分的資料列進行聚合
df.groupby(['key1', 'key2'])[['data2']].mean()
# 傳回 DataFrame
Out[17]:
In [18]:
df.groupby(['key1', 'key2'])['data2'].mean()
# 傳回 Series
Out[18]:
In [19]:
people = DataFrame(np.random.randn(5, 5),
columns=['a', 'b', 'c', 'd', 'e'],
index=['Joe', 'Steve', 'Wes', 'Jim', 'Travis'])
people.ix[2:3, ['b', 'c']] = np.nan
people
Out[19]:
In [20]:
# 已經知道 列的分組關係
mapping = {'a': 'red', 'b': 'red', 'c': 'blue',
'd': 'blue', 'e': 'red', 'f' : 'orange'}
#只需要將mapping關係的字典傳給 groupby()
grouped_by_column = people.groupby(mapping, axis = 1)
grouped_by_column.sum()
Out[20]:
In [21]:
map_series = Series(mapping)
map_series
Out[21]:
In [22]:
# 也可以將mapping關係的Series物件傳給 groupby()
grouped_by_column = people.groupby(map_series, axis = 1)
grouped_by_column.sum()
Out[22]:
In [23]:
people
Out[23]:
In [24]:
# 被當作分組鍵的函數都會在各個索引值上被調用一次,返回值就被當作分組名稱
people.groupby(len).mean()
Out[24]:
In [25]:
# 函數、列表、字典、Series都可以混用,因為最後都會被轉換為數組
key_list = ['one', 'one', 'one', 'two', 'two']
people.groupby([len, key_list]).min()
Out[25]:
In [26]:
# 根據索引級別分組
# 要依據層次化索引來分組聚合,只需要透過 level參數即可
columns = pd.MultiIndex.from_arrays([['US', 'US', 'US', 'JP', 'JP'],
[1, 3, 5, 1, 3]], names=['cty', 'tenor'])
hier_df = DataFrame(np.random.randn(4, 5), columns=columns)
hier_df
Out[26]:
In [27]:
hier_df.groupby(level = 'cty', axis = 1).count()
Out[27]:
In [28]:
# 可以自訂一聚合方法。聚合方法會對每一個分組之後的group操作一次
df
Out[28]:
In [29]:
grouped = df.groupby('key1')
for name, group in grouped:
print(name)
print(group)
In [30]:
type(grouped)
Out[30]:
In [31]:
for name, group in grouped['data1']:
print(name)
print(group)
In [32]:
# Series, DataFrame的方法都可以施加在 group上
# quantile 是 Series的方法
grouped = df.groupby('key1')
grouped['data1'].quantile(0.9)
Out[32]:
In [33]:
# 透過 aggregate()方法,可以使用自訂函式
def peak_to_peak(arr):
return arr.max() - arr.min()
grouped.aggregate(peak_to_peak)
# 會對每一個 pandas.core.groupby.DataFrameGroupBy(grouped)中的 DataFrame 中的 Series 做一次指定的 aggregate (在這邊是 peak_to_peak()) 運算
Out[33]:
In [34]:
# describe 也可以用
grouped.describe()
Out[34]:
In [35]:
grouped.mean()
# 會對每一個 pandas.core.groupby.DataFrameGroupBy中的 DataFrame 中的 Series 做一次指定的 aggregate (在這邊是 mean()) 運算
Out[35]:
In [36]:
tips = pd.read_csv('../data/tips.csv')
tips['tip_total_ratio'] = tips['tip'] / tips['total_bill']
tips[:5]
Out[36]:
In [37]:
# 對不同的列使用不同的聚合函數
grouped = tips.groupby(['sex', 'smoker'])
grouped_pct = grouped['tip_total_ratio']
for name, group in grouped_pct:
print(name)
print(group.tail(3))
In [38]:
grouped_pct.agg('mean')
Out[38]:
In [39]:
grouped_pct.aggregate('mean')
Out[39]:
In [40]:
# 傳入一組函數或函數名,得到的DataFrame的列就會以相應的函數命名
grouped_pct.agg(['mean', 'std', peak_to_peak])
Out[40]:
In [41]:
# 如果傳入一個由(name, function)的元組列表,則各元組的第一個元素就會被當作DataFrame的 column名稱
grouped_pct.agg([('foo', 'mean'), ('bar', np.std)])
Out[41]:
In [42]:
# 對於 DataFrame,還可以定義使用多個函數
functions = ['count', 'mean', 'max']
result = grouped['tip_total_ratio', 'total_bill'].agg(functions)
result
Out[42]:
In [43]:
result['tip_total_ratio']
Out[43]:
In [44]:
# 自訂一結果的列名稱
functions = [('Counts', 'count'), ('Mean', 'mean'), ('Max', 'max')]
result = grouped['tip_total_ratio', 'total_bill'].agg(functions)
result
Out[44]:
In [45]:
# 對於 DataFrame,還可以定義不同列使用不同的函數
# 傳入一個名稱與函數的字典
functions = {'tip_total_ratio': np.max, 'total_bill': np.min}
result = grouped.agg(functions)
result
Out[45]:
In [46]:
# 對於 DataFrame,還可以定義不同列使用不同的函數
functions = {'tip_total_ratio': (np.max, np.min),
'size': ['sum', 'min']}
result = grouped.agg(functions)
result
Out[46]:
In [47]:
# 透過 as_index = False,分組鍵不要成為索引
tips.groupby(['sex', 'smoker'], as_index = False).mean()
Out[47]:
In [48]:
tips.groupby(['sex', 'smoker']).mean()
Out[48]:
In [49]:
# 聚合運算 是數據轉換的一種特例
# 為df增加一列 用於存放各索引分組平均值
df
Out[49]:
In [50]:
# 計算分組mean
k1_means = df.groupby('key1').mean().add_prefix('mean_')
k1_means
Out[50]:
In [51]:
# merge
pd.merge(df, k1_means, left_on = 'key1', right_index = True)
Out[51]:
In [52]:
# 使用 transform()
people
Out[52]:
In [53]:
key = ['one', 'two', 'one', 'two', 'one']
people.groupby(key).mean()
Out[53]:
In [54]:
# 使用 transform(),將分組結果又放到各個row中(使用廣播的方式)
people.groupby(key).transform(np.mean)
Out[54]:
In [55]:
# 可以套用各種自訂函式
# 距平均化函數
def demean(arr):
return arr - arr.mean()
demeaned = people.groupby(key).transform(demean)
demeaned
Out[55]:
In [56]:
demeaned.groupby(key).transform(np.mean).applymap(lambda x: '{0:.5f}'.format(x))
Out[56]:
Difference between map, applymap and apply methods in Pandas
apply: 對 整個DataFrame(單一group) 實施一次
applymap: 對 DataFrame 的每個 儲存格 實施一次
map: 是 Series 的 function,對 Series 的每個 數值 實施一次
In [57]:
# apply 會將資料拆分成多個片段,對各個片段調用函式,最後再組合各個結果
def top(df, n = 5, column = 'tip_total_ratio'):
return df.sort_values(by = column)[-n:]
In [58]:
tips.tail()
Out[58]:
In [59]:
top(tips, n = 6)
Out[59]:
In [60]:
# 使用 apply() 來施加 自訂函式
tips.groupby('smoker').apply(top)
Out[60]:
In [61]:
# 自訂函式所需要的參數,可以放在後面一起傳入
tips.groupby(['smoker', 'day']).apply(top, n = 1, column = 'total_bill')
Out[61]:
In [62]:
result = tips.groupby(['smoker',])['total_bill'].describe()
result
Out[62]:
In [63]:
result.unstack('smoker')
Out[63]:
In [64]:
# 設定 group_keys = False,不讓分組鍵成為row索引
tips.groupby('smoker', group_keys = False).apply(top)
Out[64]:
In [65]:
tips.groupby('smoker', group_keys = True).apply(top)
Out[65]:
In [66]:
df = DataFrame({'category': ['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b'],
'data': np.random.randn(8),
'weights': np.random.rand(8)})
df
Out[66]:
In [67]:
# 計算分組加權平均數
get_wavg = lambda g: np.average(g.data * g.weights)
# 每個分組施以 get_wavg
df.groupby('category').apply(get_wavg)
Out[67]:
In [68]:
# Yahoo Finance
close_px = pd.read_csv('../data/stock_px.csv',
parse_dates = True, index_col = 0)
close_px[:6]
Out[68]:
In [69]:
# 計算 日收益率 與 SPX之間的年度相關係數組成的DataFrame
rets = close_px.pct_change().dropna()
rets[:6]
Out[69]:
In [70]:
# 與 SPX之間的相關係數
spx_corr = lambda g: g.corrwith(g.SPX)
# 以年度區分
by_year = rets.groupby(lambda x: x.year)
# 計算分組與 SPX的 corr
by_year.apply(spx_corr)
Out[70]:
In [71]:
# 也可以計算 列與列之間的相關係數
by_year.apply(lambda g: g.AAPL.corr(g.MSFT))
Out[71]:
In [72]:
tips = pd.read_csv('../data/tips.csv')
tips['tip_pct'] = tips['tip'] / tips['total_bill']
tips[:5]
Out[72]:
In [73]:
# DataFrame 本身就有 pivot_table()方法,預設的 aggregate function 是 average
tips.pivot_table(index = ['sex', 'smoker'])
Out[73]:
In [74]:
# 只聚合 tip_pct, size,而且想根據day來分組
# margins = True , 添加分項小計
tips.pivot_table(values = ['tip_pct', 'size'], index = ['sex', 'day'], columns = 'smoker', margins = True)
Out[74]:
In [75]:
tips.pivot_table(values = ['tip_pct'], index = ['sex', 'smoker'], columns = 'day', margins = True)
Out[75]:
In [76]:
# 也可傳入指定的 aggregate function (參數 aggfunc)
tips.pivot_table(values = ['tip_pct'], index = ['sex', 'smoker'], columns = 'day', margins = True, aggfunc = len)
Out[76]:
In [77]:
# 如果存在空的組合(NA),可以指定 fill_value參數,自動填入空缺值
tips.pivot_table(values = ['size'], index = ['time', 'sex', 'smoker'], columns = 'day', margins = True, aggfunc = sum, fill_value = 0)
Out[77]:
In [78]:
data = DataFrame(
{'Sample': list(range(1, 11)),
'Gender': [random.choice(['Female', 'Male']) for i in range(10)],
'Handedness': [random.choice(['Right-handed', 'Left-handed']) for i in range(10)]
},
columns = ['Sample', 'Gender', 'Handedness'])
data
Out[78]:
In [79]:
# 用 crosstab() 方法
pd.crosstab(data.Gender, data.Handedness, margins = True)
Out[79]:
In [80]:
# crosstab()方法的參數值可以是 數組
pd.crosstab(index = [tips.time, tips.day], columns = tips.smoker, margins = True)
Out[80]: